package gov.nasa.anml.lifted;

import java.util.ArrayList;

import gov.nasa.anml.PDDL;
import gov.nasa.anml.State;
import gov.nasa.anml.PDDL.*;
import gov.nasa.anml.utility.*;


// the better solution is to introduce explicit Cast expressions

// also, this should be an operation class that does every operation
// and condition should be the conversion of expressions to conditions, i.e., 
// conditions == expression statements (<expression> ';')

// In line with that, to convert float expressions into conditions (e.g.),
// in pddl, one can write (== (* <float-expression> 0) 0)
// as that ensures that all the referenced functions are defined, and perhaps
// also that the operations don't result in NaN.  Or if multiplication is verboten
// (?why?) then one can do (== (- <e> <e>) 0) where <e> == <float-expression>
public class OpBinary<I extends SimpleObject<? super I>,V extends SimpleObject<? super V>,S extends SimpleObject<? super S>> extends BinaryExpression<I,V,S> {
	public Op op;
	public TypeCode baseType;

	public OpBinary(TypeCode baseType, Op op, Expression<I,S> l, Expression<I,?> r) {
		super(l,r);
		this.op = op;
		this.baseType = baseType;
	}
	
	public <I extends SimpleObject<? super I>,S extends SimpleObject<? super S>>
		OpBinary<I,?,S> make(Op o, Expression<I,S> l, Expression<I,?> r) {
		switch(o) {
			case equal:
			case greaterThan:
			case greaterThanE:
			case lessThan:
			case lessThanE:
				return new OpBinary(TypeCode.Boolean,o,l,r);
			default:
				return new OpBinary(l.typeCode(),o,l,r);
		}
	}


	// should implement following in expression
	// and then do leftSide.toInfix() etc.
	public String toInfix() {
		StringBuilder s = new StringBuilder();
		s.append('(').append(left).
		  append(' ').append(op).
		  append(' ').append(right).
		  append(')');
		return s.toString();
	}
	
	public String toPrefix() {
		StringBuilder s = new StringBuilder();
		s.append('(').append(op).
		  append(' ').append(left).
		  append(' ').append(right).
		  append(')');
		return s.toString();
	}
	
	public String toPostfix() {
		StringBuilder s = new StringBuilder();
		s.append('(').append(left).
		  append(' ').append(right).
		  append(' ').append(op).
		  append(')');
		return s.toString();
	}

	public V value(State s) {
		I l = left.value(s);
		I r = right.value(s);
		if (l == null || r == null) 
			return null;
		switch(left.typeCode()) {
		case Boolean:
			return (V) op.eval((SimpleBoolean)l,(SimpleBoolean)r);
		case Byte:
			break;
		case Short:
			break;
		case Integer:
			return (V) op.eval((SimpleInteger)l,(SimpleInteger)r);
		case Long:
			break;
		case Float:
			return (V) op.eval((SimpleFloat)l,(SimpleFloat)r);
		case Double:
			break;
		case String:
			return (V) op.eval((SimpleString)l,(SimpleString)r);
		case Symbol:
			break;
		case Vector:
			break;
		case Object:
			break;
		}
		return null;
	}

	public TypeCode typeCode() {
		return baseType;
	}

	public History<S> storage(State p, State c) {
		return null;
	}
	
	public boolean apply(State p, int contextID, State c) {
		if (baseType != TypeCode.Boolean)
			return false;
		I l = left.value(p);
		I r = right.value(p);
		if (l == null || r == null)
			return false;
		SimpleBoolean ret = null;
		switch(left.typeCode()) {
		case Boolean:
			ret = (SimpleBoolean) op.eval((SimpleBoolean)l,(SimpleBoolean)r);
			break;
		case Byte:
			break;
		case Short:
			break;
		case Integer:
			ret = (SimpleBoolean) op.eval((SimpleInteger)l,(SimpleInteger)r);
		case Long:
			break;
		case Float:
			ret = (SimpleBoolean) op.eval((SimpleFloat)l,(SimpleFloat)r);
		case Double:
			break;
		case String:
			ret = (SimpleBoolean) op.eval((SimpleString)l,(SimpleString)r);
		case Symbol:
			break;
		case Vector:
			break;
		case Object:
			break;
		}
		if (ret != ANMLBoolean.True)
			return false;
		return true;
	}

	public transient PDDL.Expression asPDDLExpression;

	public PDDL.Expression translateExpr(PDDL pddl, Interval unit) {
		if (asPDDLExpression != null)
			return asPDDLExpression;
		TypeCode code = left.typeCode();
		switch(baseType) {
		case Boolean:
			switch(code) {
			case Boolean:
				PDDL.BooleanExpression lb = (PDDL.BooleanExpression) left.translateExpr(pddl,unit);
				PDDL.BooleanExpression rb = (PDDL.BooleanExpression) right.translateExpr(pddl,unit);
				if (lb == null)
					lb = pddl.TrueRef;
				if (rb == null)
					rb = pddl.TrueRef;
				return asPDDLExpression = pddl.makeTest(op.pddl,lb,rb);
			case Float:
				FloatExpression lf = (FloatExpression) left.translateExpr(pddl,unit);
				FloatExpression rf = (FloatExpression) right.translateExpr(pddl,unit);
				if (lf == null)
					lf = pddl.FloatUndefined;
				if (rf == null)
					rf = pddl.FloatUndefined;
				return asPDDLExpression = pddl.makeTest(op.pddl,lf,rf);
			default:
				System.err.println("Unsupported type: "+code+" in " + left + " --- during: " + this);
				return pddl.FalseCondition;
				// the following is not safe because I assume typeCode() == Boolean means translateExpr() instanceof BooleanExpression
				// and this line would defeat that.
				//return asPDDLExpression = pddl.wrap(op.pddl,left.translateExpr(pddl,unit),right.translateExpr(pddl,unit));
			}
		case Float:
			FloatExpression lf = (FloatExpression) left.translateExpr(pddl,unit);
			FloatExpression rf = (FloatExpression) right.translateExpr(pddl,unit);
			if (lf == null)
				lf = pddl.FloatUndefined;
			if (rf == null)
				rf = pddl.FloatUndefined;
			return asPDDLExpression = pddl.new BinaryFloatExpression(op.pddl,lf,rf);
		default:
			System.err.println("Unsupported result type: "+baseType+" --- during: " + this);
			// this too should probably just return Falsecondition, but locally it would work for a while not too.
		// ah well, to play a little safer...
			//return asPDDLExpression = pddl.new CompoundExpression(op.pddl,left.translateExpr(pddl,unit),right.translateExpr(pddl,unit));
			return pddl.FalseCondition;
		}
	}
	

}
